﻿using IniFile;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Management;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;

namespace 串口助手
{
    public partial class Form1 : Form
    {
        int gTxNum = 0;  // 发送计数器，初始化为0
        int gRxNum = 0;  // 接收计数器，初始化为0
        bool gTimestampFlag = true;  // 时间戳标志，初始为真

        const int TimerRxAutoNewlinePeriod = 5;  // 定义自动换行的定时器周期为5毫秒
        System.Timers.Timer gTimer_RxAutoNewline = new System.Timers.Timer(TimerRxAutoNewlinePeriod);  // 创建定时器实例，设置周期

        bool gTxShowFlag = false;  // 发送显示标志，初始为假

        bool threadTableTx_RunFlag = false;  // 线程运行标志，初始为假
        Thread threadTableTx = null;  // 发送线程，初始化为null

        Config gCfg = new Config();  // 创建配置管理实例
        ContextMenu contextMenu = new ContextMenu();  // 创建上下文菜单实例
        CommLog commLog = new CommLog();  // 创建通信日志实例
        public SuperMsg SuperMsgCurrent = null;  // 当前超级消息，初始化为null
        List<object> SuperMsgList = new List<object>();  // 超级消息列表，初始化为空列表
        RealTimeCurve realTimeCurve = new RealTimeCurve();  // 实时曲线实例
        AutoTx AutoTx = new AutoTx();  // 自动发送实例

        /// <summary>
        /// 处理定时器超时事件：当定时器触发时执行此方法
        /// </summary>
        /// <param name="sender">触发事件的对象</param>
        /// <param name="e">事件参数</param>
        private void Timer_RxAutoNewline_OutHandle(object sender, EventArgs e)
        {
            // 处理定时器超时事件：如果最新行已显示数据，则进行换行
            // if (richTextBox_Rx.Text[richTextBox_Rx.Text.Length - 1] != '\n')
            // if (!gTimestampFlag)
            //    WriteRxMsg(Color.White, System.Environment.NewLine);

            gTimestampFlag = true;  // 重置时间戳标志为真
            gTimer_RxAutoNewline.Stop();  // 停止定时器
        }

        /// <summary>
        /// 初始化接收自动换行定时器
        /// </summary>
        /// <param name="delay">定时器触发的时间间隔，单位为毫秒</param>
        private void Timer_RxAutoNewLine_Init(int delay)
        {
            gTimer_RxAutoNewline.Interval = delay;
            gTimer_RxAutoNewline.Elapsed += new System.Timers.ElapsedEventHandler(Timer_RxAutoNewline_OutHandle);
            gTimer_RxAutoNewline.AutoReset = true;
        }

        /// <summary>
        /// TCP 客户端接收数据的方法
        /// </summary>
        /// <param name="sender">触发该方法的 Socket 连接</param>
        private void TCP_ClientRx(object sender)
        {
            if (!(sender is Socket sockConnection))  // 检查 sender 是否为有效的 Socket
                return;

            byte[] buffMsgRec = new byte[1024 * 1024];  // 定义接收数据的缓冲区，大小为 1 MB
            int len = 0;  // 接收到的数据长度
            sockConnection.ReceiveTimeout = 500;  // 设置接收超时时间为 500 毫秒

            while (true)  // 持续循环接收数据
            {
                if (gBootloaderFlag)  // 检查是否处于引导加载程序状态
                {
                    Thread.Sleep(100);  // 暂停 100 毫秒
                    continue;  // 继续下一次循环
                }

                try
                {
                    len = sockConnection.Receive(buffMsgRec);  // 接收数据并获取长度
                                                               // 处理接收到的数据
                    if (数据接收_显示_解析_输出日志(sender, "tcp", ref buffMsgRec, len) == false)
                        continue;  // 如果处理失败，继续下一次循环
                }
                catch (Exception ex)
                {
                    // 记录异常信息以便调试
                    Console.WriteLine($"接收数据时发生异常: {ex.Message}");
                }
            }
        }

        /// <summary>
        /// Form1 的构造函数
        /// </summary>
        public Form1()
        {
            InitializeComponent();  // 初始化窗体及其组件

            // 允许跨线程调用控件的方法（默认情况下会检查非法调用并抛出异常）
            System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;
        }

        /// <summary>
        /// 表单加载事件处理程序
        /// </summary>
        /// <param name="sender">事件源</param>
        /// <param name="e">事件参数</param>
        private void Form1_Load(object sender, EventArgs e)
        {
            try
            {
                // 更新串口下拉框并选择第一个可用串口
                SerialPortNumberUpadta(toolStripComboBox_SerialPort);
                toolStripComboBox_SerialPort.Text = System.IO.Ports.SerialPort.GetPortNames()[0];
            }

            catch 
            {
                // 异常处理（可以记录日志）
            }

            // 初始化主通信类型和按钮文本
            masterComm.type = "";
            toolStripButton_Master.Text = "开启";

            // 添加失去焦点事件处理程序
            toolStripTextBox自动换行定时器周期.LostFocus += new EventHandler(toolStripTextBox自动换行定时器周期_LostFocusClick);

            // 设置实时曲线控件并添加到页面
            realTimeCurve.TopLevel = false;
            realTimeCurve.Dock = DockStyle.Fill;
            tabPage5.Controls.Add(realTimeCurve);
            realTimeCurve.Show();

            // 加载设定和配置
            加载设定();
            gCfg.TableTxCreate(tableLayoutPanel1, tableLayoutPanel2);
            splitContainer1.Size = new Size(246, (20 * gCfg.TxListNum) + 200);
            gCfg.TxListFirstLoad();

            // 注册事件
            TxListEventReg();
            加载缓存的设备();
            toolStripButton_log.Click += new System.EventHandler(commLog.ButtonLogClick);
            contextMenu.ContextMenuTxListInit();
            contextMenu.ContextMenu_CreateSuperMsgClickCfg(CreateSuperMsg_Handler);
            contextMenu.TxListSimpleMenu.Opening += new CancelEventHandler(TxListSimpleMenu_Opening);

            // 设置上下文菜单
            richTextBox_Rx.ContextMenuStrip = contextMenu.RxShowMenu;
            flowLayoutPanel_SuperMsg.ContextMenuStrip = contextMenu.CreateSuperMsgMenu;

            // 添加失去焦点事件处理程序
            textBoxTxListName.LostFocus += new EventHandler(textBoxTxListName_LostFocusClick);
            textBoxTxListMsg.LostFocus += new EventHandler(textBoxTxListMsg_LostFocusClick);
        }

        /// <summary>
        /// 注册事件处理程序以响应用户与 TX 列表控件的交互
        /// </summary>
        private void TxListEventReg()
        {
            // 为每个文本框注册 KeyUp 和 GotFocus 事件处理程序
            foreach (var textbox in gCfg.TextboxTX)
            {
                // 当键盘按键释放时触发的事件
                textbox.KeyUp += (s, e) => TextboxTX_KeyUp(s, e);
                // 当文本框获得焦点时触发的事件
                textbox.GotFocus += (s, e) => TextboxTX_GotFocusClick(s, e);
            }

            // 为每个按钮注册 Click 事件处理程序
            foreach (var button in gCfg.Button)
            {
                // 当按钮被点击时触发的事件
                button.Click += (s, e) => button_Click(s, e);
                // 为按钮设置上下文菜单
                button.ContextMenuStrip = contextMenu.TxListMenu;
            }

            // 为每个复选框注册 MouseHover 事件处理程序
            foreach (var checkbox in gCfg.Checkbox)
            {
                // 当鼠标悬停在复选框上时触发的事件
                checkbox.MouseHover += (s, e) => TxListHelp_MouseHover(s, e);
            }

            // 为每个定时器文本框注册 MouseHover 事件处理程序
            foreach (var timer in gCfg.TextboxTimer)
            {
                // 当鼠标悬停在定时器文本框上时触发的事件
                timer.MouseHover += (s, e) => TxListHelp_MouseHover(s, e);
            }
        }



        /// <summary>
        /// 获取所有的串口设备号
        /// </summary>
        /// <param name="combo"></param>
        private static void SerialPortNumberUpadta(ToolStripComboBox combo)
        {
            // 清除当前组合框的下拉菜单内容
            combo.Items.Clear();

            //combo.Items.AddRange(System.IO.Ports.SerialPort.GetPortNames());
            //combo.Items.Add("TCP Client");
            //combo.Items.Add("TCP Server");

            // 使用 ManagementObjectSearcher 查询连接的串口设备
            using (ManagementObjectSearcher searcher = new ManagementObjectSearcher
                ("select * from Win32_PnPEntity where Name like '%(COM%'"))
            {
                // 获取硬件信息
                var hardInfos = searcher.Get();

                // 遍历每个硬件信息对象
                foreach (var hardInfo in hardInfos)
                {
                    // 检查硬件名称属性是否不为 null
                    if (hardInfo.Properties["Name"].Value != null)
                    {
                        // 获取设备名称并添加到组合框
                        string deviceName = hardInfo.Properties["Name"].Value.ToString();
                        combo.Items.Add(deviceName);
                    }
                }
            }

            // 添加 TCP Client 选项到组合框
            combo.Items.Add("TCP Client");
        }

        private void TxMsg(byte[] data)
        {
            //接收区显示发送内容
            string show = "";
            DataTypeConversion Conversion = new DataTypeConversion();
            Conversion.ByteAddMsgTail(MasterMsgEnd.Text, ref data);
            if (gTxShowFlag && gBootloaderFlag == false)
            {
                show += DateTime.Now.ToString("HH:mm:ss.fff") + ">>";
                show += Conversion.ByteToString(toolStripButton_TxHEX.Text, data, data.Length);
                commLog.LogWriteMsg(show);
                SendMsgEcho(System.Environment.NewLine + show);
            }

            if (masterComm.type == "TCP Client")
                masterComm.tcp.Send(data, 0, data.Length, SocketFlags.None);
            else
            {
                masterComm.uart.Write(data, 0, data.Length);
            }

            gTxNum += data.Length;
            Label_Status.Text = "OPEN    TX:" + gTxNum + "    RX:" + gRxNum;
        }

        /// <summary>
        /// 直接发送报文，无解析动作
        /// </summary>
        /// <param name="data"></param>
        /// <param name="data_type"></param>
        private bool CommSendData(MultiCommunication_t dev, string data, string data_type)
        {
            // 如果传入的数据为空，直接返回 true
            if (data == "")
                return true;

            try
            {
                int length = 0;
                DataTypeConversion dataType = new DataTypeConversion();

                // 将字符串数据转换为字节数组
                byte[] TxBuf = dataType.StringToByte(data_type, data);
                if (TxBuf == null)
                    return false;

                // 处理报尾，更新数据长度
                length = dataType.ByteAddMsgTail(dev.MsgTailType, ref TxBuf);

                // 准备显示发送内容
                string show = "";
                if (gTxShowFlag && gBootloaderFlag == false)
                {
                    // 如果设备名称不为 "S0"，则在前面加上设备名
                    if (dev.name != "S0")
                        show = "[" + dev.name + "]";

                    // 添加当前时间和发送内容到显示字符串
                    show += DateTime.Now.ToString("HH:mm:ss.fff") + ">>";
                    show += dataType.ByteToString(data_type, TxBuf, length);
                    commLog.LogWriteMsg(show);// 写入日志
                    SendMsgEcho(System.Environment.NewLine + show);// 显示发送回显
                }

                // 如果当前有超信息显示，追加发送内容
                if (SuperMsgCurrent != null)
                {
                    textBox_SuperMsgRxShow.AppendText(System.Environment.NewLine + show);
                }

                //发送数据
                if (dev.type == "TCP Client")
                {
                    // 分块发送数据，以避免一次发送过大的数据
                    for (int i = 0; i < length; i += 4096)
                    {
                        if (i + 4096 > length)
                            dev.tcp.Send(TxBuf, i, length - i, SocketFlags.None);
                        else
                            dev.tcp.Send(TxBuf, i, 4096, SocketFlags.None);
                    }
                }
                else// 对于 UART 设备，直接发送
                    dev.uart.Write(TxBuf, 0, length);

                // 更新发送计数
                gTxNum += length;
                Label_Status.Text = "OPEN    TX:" + gTxNum + "    RX:" + gRxNum;
                return true;
            }
            catch 
            {
                // 记录异常信息（根据需要可以添加日志记录）
            }

            return false;// 如果发生异常，返回 false
        }

        /// <summary>
        /// 发送超级消息数据到指定通信设备，并在接收显示框中追加消息功能信息。
        /// </summary>
        /// <param name="data">要发送的数据，以字符串形式表示。</param>
        /// <param name="msgFunc">与消息相关的功能描述，用于显示在接收框中。</param>
        public void SuperMsgSendData(string data, string msgFunc)
        {
            // 检查是否处于开启状态，如果是，则直接返回，不发送数据
            if (toolStripButton_Master.Text == "开启")
                return;

            // 设置消息尾部类型
            masterComm.MsgTailType = MasterMsgEnd.Text;

            // 调用 CommSendData 方法发送数据，数据格式为 HEX
            CommSendData(masterComm, data, "HEX");

            // 在超级消息接收显示框中追加显示消息功能信息
            textBox_SuperMsgRxShow.AppendText("     " + msgFunc);
        }

        /// <summary>
        /// 发送单个消息到指定设备，支持通过特定分隔符拆分多个消息。
        /// </summary>
        /// <param name="data">要发送的消息字符串，可能包含多个设备的消息。</param>
        /// <param name="data_type">发送数据的类型，如 HEX 或其他。</param>
        private void CommSingleMsgSend(string data, string data_type)
        {
            // 如果数据为空，则直接返回
            if (data == "")
                return;

            try
            {
                string txBuf = "";
                // 拆分当前报文，可能有多个设备的报文，使用 "$$" 作为分隔符
                string[] msgAll = data.Split(new string[] { "$$" }, StringSplitOptions.RemoveEmptyEntries);

                // 遍历拆分后的每条消息
                for (int pos = 0; pos < msgAll.Length; pos++)
                {
                    // 进一步拆分每条消息，以 "$" 作为分隔符
                    string[] devData = msgAll[pos].Split('$');
                    // 获取待发送的报文内容
                    if (devData.Length == 1)
                        txBuf = devData[0]; // 如果只有一部分，直接取第一个
                    else
                        txBuf = devData[1]; // 否则取第二部分

                    // 确定当前的发送端口，主设备
                    if (devData.Length == 1 ||
                        (devData.Length == 2 && devData[0].ToUpper() == "S0"))
                    {
                        // 设置消息尾部类型
                        masterComm.MsgTailType = MasterMsgEnd.Text;
                        // 发送数据
                        CommSendData(masterComm, txBuf, data_type);
                    }
                    else
                    {
                        // 查找其他设备并发送消息
                        devData[0] = devData[0].ToUpper(); // 统一转换为大写
                        foreach (MultiCommunication_t p in listMultiComm)
                        {
                            // 匹配设备名称并发送
                            if (p.name == devData[0])
                            {
                                CommSendData(p, txBuf, p.hex);
                                break; // 找到后退出循环
                            }
                        }
                    }
                }
            }
            catch
            {
                // 处理异常但不做任何事情
            }
        }




        private long totalBytesReceived = 0; // 记录接收到的总字节数
        private DateTime startTime; // 记录开始接收的时间
        private bool isReceiving = false; // 接收状态标志


        /// <summary>
        /// 接收串口数据并计算接收频率。
        /// </summary>
        /// <param name="sender">触发事件的串口对象。</param>
        /// <param name="data">接收到的数据缓冲区。</param>
        /// <returns>实际接收到的字节数。</returns>
        private int 串口接收数据(object sender, ref byte[] data)
        {
            int length = 0; // 移动到方法开始处，方便在 finally 块中使用

            try
            {
                System.IO.Ports.SerialPort uart = (System.IO.Ports.SerialPort)sender;
                richTextBox_Rx.Text = "";           //注意田
                // 如果开始接收，初始化开始时间
                if (!isReceiving)
                {
                    isReceiving = true; // 标记为正在接收
                    totalBytesReceived = 0; // 重置接收字节数
                    startTime = DateTime.Now; // 记录开始接收的时间
                }

                // 如果没有数据可读，直接返回
                if (uart.BytesToRead == 0)
                    return 0;

                // 读取数据，避免使用延时
                length = uart.BytesToRead > 1024 ? 1024 : uart.BytesToRead; // 限制读取字节数
                data = new byte[length + 1];    // 预留一个字节
                uart.Read(data, 0, length);     // 读取数据

                totalBytesReceived += length;   // 累加接收到的字节数

                // 计算接收频率
                TimeSpan elapsed = DateTime.Now - startTime; // 计算经过的时间
                double frequency = 0;

                if (elapsed.TotalSeconds > 0)   // 确保不除以零
                {
                    frequency = totalBytesReceived / elapsed.TotalSeconds; // 计算字节每秒
                }

                // 更新接收频率到界面上
                UpdateReceiveFrequencyDisplay(frequency);

                return length;
            }
            catch (Exception ex)
            {
                // 记录异常信息，方便后续调试
                Console.WriteLine($"接收数据时发生错误: {ex.Message}");
            }
            finally
            {
                // 在结束接收时重置状态
                if (isReceiving && length == 0) // 这里可以根据你的逻辑判断是否结束接收
                {
                    isReceiving = false; // 标记为未接收
                    totalBytesReceived = 0; // 重置接收字节数
                }
            }
            return 0; // 默认返回值
        }

        /// <summary>
        /// 更新接收频率的显示。
        /// </summary>
        /// <param name="frequency">接收频率，单位为字节/秒。</param>
        private void UpdateReceiveFrequencyDisplay(double frequency)
        {
            // 使用千位分隔符格式化频率
            string formattedFrequency = frequency.ToString("N0"); // "N0" 格式表示千位分隔符，且不保留小数

            // 使用 Invoke 确保在 UI 线程上更新控件
            if (lblReceiveFrequency.InvokeRequired) // 检查是否需要跨线程调用
            {
                // 在UI线程上更新标签的文本
                lblReceiveFrequency.Invoke(new Action(() =>
                    lblReceiveFrequency.Text = $"接收频率: {formattedFrequency} 字节/秒"));
            }
            else
            {
                // 在当前线程上直接更新标签的文本
                lblReceiveFrequency.Text = $"接收频率: {formattedFrequency} 字节/秒";
            }
        }

        /// <summary>
        /// 处理发送数据的操作，检查输入框并通过主设备发送数据。
        /// </summary>
        private void CommMasterButtonSend()
        {
            try
            {
                // 检查发送文本框是否为空，如果为空则直接返回
                if (TextBox_Tx.Text == "")
                    return;

                // 设置消息尾部类型
                masterComm.MsgTailType = MasterMsgEnd.Text;

                // 调用CommSendData方法发送数据
                CommSendData(masterComm, TextBox_Tx.Text, toolStripButton_TxHEX.Text);
            }
            catch
            {
                // 捕获异常但不进行处理，可以考虑记录日志或提示用户
            }

            // 检查接收清空按钮的状态，如果禁用则启用并改变背景色
            if (toolStripButton_RecClear.Enabled == false)
            {
                toolStripButton_RecClear.Enabled = true; // 启用接收清空按钮
                toolStripButton_RecClear.BackColor = Color.Gold; // 更改按钮背景色
            }
        }



        /// <summary>
        /// 处理主设备按钮的点击事件，负责开启或关闭设备通信。
        /// </summary>
        /// <param name="sender">事件源</param>
        /// <param name="e">事件参数</param>
        private void toolStripButton_Master_Click(object sender, EventArgs e)
        {
            // 注册设备，检查是否成功
            if (设备注册("S0",
                           toolStripButton_Master.Text,
                           toolStripComboBox_SerialPort.Text,
                           toolStripComboBox_BaudRate.Text,
                           toolStripTextBox_TCPPort.Text,
                           MasterMsgEnd.Text,
                           "HEX",
                           "") == false)
                return;

            // 判断当前按钮状态，决定是开启还是关闭通信
            if (toolStripButton_Master.Text == "开启")
            {
                // 查找名为"S0"的通信设备
                foreach (MultiCommunication_t p in listMultiComm)
                {
                    if (p.name == "S0")
                    {
                        masterComm.type = p.type; // 设置通信类型
                        masterComm.name = p.name; // 设置设备名称
                        masterComm.tcp = p.tcp;   // 设置TCP连接
                        masterComm.uart = p.uart; // 设置串口连接
                        break;
                    }
                }

                try
                {
                    // 禁用相关的输入框，防止用户在通信过程中修改设置
                    toolStripComboBox_SerialPort.Enabled = false;
                    toolStripComboBox_BaudRate.Enabled = false;
                    toolStripTextBox_TCPPort.Enabled = false;

                    // 启用其他功能按钮，更新UI状态
                    toolStripButton_BootOn.Enabled = true;
                    toolStripButton_Master.BackColor = Color.Tomato;
                    toolStripButton_Master.Image = Properties.Resources.关闭; // 更新图标
                    toolStripButton_Master.Text = "关闭"; // 更新按钮文本
                    button_Tx.BackColor = Color.Lime; // 更新发送按钮颜色

                    Label_Status.Text = "OPEN"; // 更新状态标签
                }
                catch
                {
                    MessageBox.Show("端口打开失败，请检查！", "ERROR"); // 弹出错误提示
                }
            }
            else // 关闭通信
            {
                try
                {
                    // 关闭bootloader相关的线程
                    if (threadBootloader != null)
                        threadBootloader.Abort();
                    if (threadBootloaderDownload != null)
                        threadBootloaderDownload.Abort();
                    if (threadBootloaderErase != null)
                        threadBootloaderErase.Abort();

                    // 关闭发送列表的线程
                    checkBox_TimSend.Enabled = true; // 启用定时发送复选框
                    threadTableTx_RunFlag = false; // 停止发送列表线程
                    if (threadTableTx != null)
                        threadTableTx.Abort();

                    // 重置主设备通信信息
                    masterComm.type = "";
                    toolStripComboBox_SerialPort.Enabled = true; // 重新启用串口选择框
                    toolStripComboBox_BaudRate.Enabled = true; // 重新启用波特率选择框
                    toolStripTextBox_TCPPort.Enabled = true; // 重新启用TCP端口输入框

                    // 更新按钮状态和UI
                    toolStripButton_BootOn.Enabled = false;
                    toolStripButton_Master.BackColor = Color.LimeGreen; // 更新按钮颜色
                    toolStripButton_Master.Image = Properties.Resources.开启; // 更新图标
                    toolStripButton_Master.Text = "开启"; // 更新按钮文本
                    button_Tx.BackColor = Color.DeepSkyBlue; // 更新发送按钮颜色

                    Label_Status.Text = "CLOSE"; // 更新状态标签
                    timer_Send.Enabled = false; // 停止发送定时器
                    gTimer_RxAutoNewline.Stop(); // 停止自动换行定时器
                }
                catch
                {
                    MessageBox.Show("端口关闭异常！", "ERROR"); // 弹出错误提示
                }
            }
        }

        /// <summary>
        /// 处理发送按钮的点击事件，根据主设备状态决定发送数据或打开设备通信.
        /// </summary>
        /// <param name="sender">事件源</param>
        /// <param name="e">事件参数</param>
        private void button_Tx_Click(object sender, EventArgs e)
        {
            // 检查主设备的状态，如果设备已开启
            if (toolStripButton_Master.Text == "关闭")
            {
                // 调用发送数据的方法
                CommMasterButtonSend();
            }
            else
            {
                // 如果设备未开启，则触发主设备按钮的点击事件以打开通信
                toolStripButton_Master_Click(toolStripButton_Master, e);
            }
        }


        /// <summary>
        /// 处理串口数据接收事件，解析接收到的数据并进行相应处理.
        /// </summary>
        /// <param name="sender">触发事件的串口对象</param>
        /// <param name="e">串口数据接收事件的参数</param>
        private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            // 检查是否处于 Bootloader 模式，如果是则返回，不进行数据处理
            if (gBootloaderFlag == true)
                return;

            // 如果当前使用超级报文，稍微等待以确保完整接收数据
            if (SuperMsgCurrent != null && (System.IO.Ports.SerialPort)sender == masterComm.uart)
                Delay(10);

            // 定义接收缓冲区
            byte[] RxBuf = new byte[1];
            // 调用串口接收数据的方法，获取接收到的数据长度
            int length = 串口接收数据(sender, ref RxBuf);

            // 如果接收到的数据长度为零，则直接返回，不进行后续处理
            if (length == 0)
                return;

            // 解析接收到的数据，并显示及输出日志
            数据接收_显示_解析_输出日志(sender, "uart", ref RxBuf, length);
        }


        /// <summary>
        /// 处理定时发送复选框状态改变事件，控制定时发送功能的开启与关闭.
        /// </summary>
        /// <param name="sender">触发事件的复选框对象</param>
        /// <param name="e">事件参数</param>
        private void checkBox_TimSend_CheckedChanged(object sender, EventArgs e)
        {
            // 如果复选框被选中，且定时发送的时间大于0，且主设备处于关闭状态
            if (checkBox_TimSend.Checked && NumUpDown_TimSend.Value > 0 && toolStripButton_Master.Text == "关闭")
            {
                // 确保定时发送的时间至少为10毫秒
                if (NumUpDown_TimSend.Value < 10)
                    NumUpDown_TimSend.Value = 10;

                // 设置定时器的间隔时间为用户配置的值
                timer_Send.Interval = (int)NumUpDown_TimSend.Value;
                // 禁用时间设置框以防止用户修改
                NumUpDown_TimSend.Enabled = false;
                // 启动定时器以开始定时发送
                timer_Send.Enabled = true;
            }
            else
            {
                // 如果条件不满足，自动取消复选框选中状态
                checkBox_TimSend.Checked = false;
                // 停止定时器
                timer_Send.Enabled = false;
                // 重新启用时间设置框以便用户可以修改
                NumUpDown_TimSend.Enabled = true;
            }
        }


        /// <summary>
        /// 处理定时器的滴答事件，用于执行定时发送操作.
        /// </summary>
        /// <param name="sender">触发事件的定时器对象</param>
        /// <param name="e">事件参数</param>
        private void timer_Send_Tick(object sender, EventArgs e)
        {
            // 检查是否不在引导加载状态，主设备是否关闭，以及文本框中是否有内容
            if (gBootloaderFlag == false && toolStripButton_Master.Text == "关闭" && TextBox_Tx.Text.Length > 0)
            {
                // 执行发送数据操作
                CommMasterButtonSend();
            }
            else if (gBootloaderFlag)
            {
                // 如果在引导加载状态，禁用定时器
                timer_Send.Enabled = false;
                // 重新启用时间设置框以便用户可以修改
                NumUpDown_TimSend.Enabled = true;
            }
        }


        /// <summary>
        /// 清除接收区域的内容并重置相关状态信息.
        /// </summary>
        /// <param name="sender">触发事件的工具条按钮对象</param>
        /// <param name="e">事件参数</param>
        private void toolStripButton_RecClear_Click(object sender, EventArgs e)
        {
            // 清空接收数据显示区域
            richTextBox_Rx.Text = "";
            textBox_SuperMsgRxShow.Text = "";

            // 重置清除按钮的背景色和可用状态
            toolStripButton_RecClear.BackColor = Color.Transparent;
            toolStripButton_RecClear.Enabled = false;

            // 重置接收和发送计数
            gTxNum = 0;
            gRxNum = 0;

            // 更新状态标签显示当前的发送和接收计数
            Label_Status.Text = "OPEN    TX:" + gTxNum + "    RX:" + gRxNum;
        }

        /// <summary>
        /// 当自动换行定时器周期文本框失去焦点时触发，更新定时器周期.
        /// </summary>
        /// <param name="sender">触发事件的文本框对象</param>
        /// <param name="e">事件参数</param>
        private void toolStripTextBox自动换行定时器周期_LostFocusClick(object sender, EventArgs e)
        {
            // 创建数据类型转换实例
            DataTypeConversion conversion = new DataTypeConversion();

            // 从文本框获取定时器周期值，转换为整数
            int delay = conversion.GetStringNumber(toolStripTextBox自动换行定时器周期.Text);

            // 初始化自动换行定时器，设置为新的周期值
            Timer_RxAutoNewLine_Init(delay);
        }


        /// <summary>
        /// 处理鼠标按下事件，保存当前的系统配置和发送/接收设置.
        /// </summary>
        /// <param name="sender">触发事件的控件</param>
        /// <param name="e">鼠标事件参数</param>
        private void toolStripButton_SaveSet_MouseDown(object sender, MouseEventArgs e)
        {
            // 清空多通道配置区的系统配置文件
            gCfg.SysCfgFileClear("多通道配置区");

            // 写入基本配置的各项参数
            gCfg.SysCfgFileWrite("基本配置", "端口", toolStripComboBox_SerialPort.Text);
            gCfg.SysCfgFileWrite("基本配置", "波特率/IP", toolStripComboBox_BaudRate.Text);
            gCfg.SysCfgFileWrite("基本配置", "TCP端口", toolStripTextBox_TCPPort.Text);
            gCfg.SysCfgFileWrite("基本配置", "Form1Size", $"{this.Size.Width},{this.Size.Height}");
            gCfg.SysCfgFileWrite("基本配置", "MasterSize", splitContainer_Master.SplitterDistance.ToString());
            gCfg.SysCfgFileWrite("基本配置", "TableTxSize", splitContainer1.SplitterDistance.ToString());

            // 写入按钮的状态信息
            gCfg.SysCfgFileWrite("基本配置", "ButtonTxHEX", $"{toolStripButton_TxHEX.Text},{toolStripButton_TxHEX.BackColor.Name}");
            gCfg.SysCfgFileWrite("基本配置", "ButtonRxHEX", $"{toolStripButton_RxHEX.Text},{toolStripButton_RxHEX.BackColor.Name}");
            gCfg.SysCfgFileWrite("基本配置", "ButtonRxAutoNewline", $"{toolStripButton_RxAutoNewline.Text},{toolStripButton_RxAutoNewline.BackColor.Name}");
            gCfg.SysCfgFileWrite("基本配置", "ButtonTimestamp", $"{toolStripButton_Timestamp.Text},{toolStripButton_Timestamp.BackColor.Name}");
            gCfg.SysCfgFileWrite("基本配置", "TableTx显隐", splitContainer_Master.Panel2Collapsed.ToString());
            gCfg.SysCfgFileWrite("基本配置", "Tx回显", toolStripLabel1.ForeColor.Name);
            gCfg.SysCfgFileWrite("基本配置", "主设备的报尾", MasterMsgEnd.Text);
            gCfg.SysCfgFileWrite("基本配置", "自动换行时间", gTimer_RxAutoNewline.Interval.ToString());

            // 保存发送区的设置
            gCfg.SysCfgFileWrite("发送区", "MasterTx", TextBox_Tx.Text);
            gCfg.SysCfgFileWrite("接收区", "RxShowTextSize", richTextBox_Rx.Font.Size.ToString());

            // 保存多通道的信息
            int cnt = 0;
            if (listMultiComm.Count > 0)
            {
                foreach (MultiCommunication_t p in listMultiComm)
                {
                    if (p.name == "S0")
                        continue;
                    gCfg.SysCfgFileWrite("多通道配置区", "设备" + cnt.ToString(), p.saveInfo);
                    cnt++;
                }
                gCfg.SysCfgFileWrite("多通道配置区", "最大设备数", cnt.ToString());
            }

            // 检查发送列表路径是否为空
            if (string.IsNullOrEmpty(gCfg.TxListPath))
            {
                MessageBox.Show("请在[设置]中'新建'或'加载'发送列表!", "注意");
                return;
            }

            // 保存发送列表的路径
            gCfg.SysCfgFileWrite("发送列表", "path", gCfg.TxListPath);
            if (string.IsNullOrEmpty(toolStripTextBox_TxListNum.Text))
                toolStripTextBox_TxListNum.Text = "64";
            gCfg.TxListSave(toolStripTextBox_TxListNum.Text);

            // 保存精简发送列表
            try
            {
                textBoxTxListName_LostFocusClick(null, null);
                textBoxTxListMsg_LostFocusClick(null, null);
                if (txListSimple_List.Count > 0)
                {
                    int tx_cnt = 0;
                    string data = "";
                    gCfg.IniWrite("TxListSimple", "按钮个数", txListSimple_List.Count.ToString(), gCfg.TxListPath);
                    foreach (TxListSimple t in txListSimple_List)
                    {
                        data = $"{t.DataType}$${t.MsgName}$";
                        if (t.Msg != null && t.Msg.Length > 0)
                        {
                            string lineStr = t.Msg[0].Replace("$", "\\\\&");
                            data += lineStr;
                            for (int lines = 1; lines < t.Msg.Length; lines++)
                            {
                                lineStr = t.Msg[lines].Replace("$", "\\\\&");
                                data += $"\\n{lineStr}";
                            }
                        }
                        gCfg.IniWrite("TxListSimple", "TX" + tx_cnt.ToString(), data, gCfg.TxListPath);
                        tx_cnt++;
                    }
                }
            }
            catch { }

            // 保存自动下发的数据
            string AutoTxData = textBox_AutoTx.Text.Replace("\r\n", "\\n");
            gCfg.IniWrite("自动下发指令", "规则", AutoTxData, gCfg.TxListPath);

            // 保存超级报文
            cnt = 0;
            int[] param_cnt = new int[SuperMsgList.Count];
            foreach (object obj in SuperMsgList)
            {
                param_cnt[cnt] = 0;
                foreach (SuperMsg.ParamClass_t p in (List<SuperMsg.ParamClass_t>)obj)
                {
                    gCfg.IniWrite("SuperMsg", "指令" + cnt.ToString() + '-' + param_cnt[cnt].ToString(), p.save_info, gCfg.TxListPath);
                    param_cnt[cnt]++;
                }
                cnt++;
            }
            gCfg.IniWrite("SuperMsg", "指令个数", cnt.ToString(), gCfg.TxListPath);
            for (int i = 0; i < cnt; i++)
            {
                gCfg.IniWrite("SuperMsg", "指令" + i.ToString(), param_cnt[i].ToString(), gCfg.TxListPath);
                gCfg.IniWrite("SuperMsg", "指令" + i.ToString() + "报文", ((List<SuperMsg.ParamClass_t>)SuperMsgList[i])[0].msg_tx, gCfg.TxListPath);
            }

            // 显示保存成功的提示
            MessageBox.Show("发送框保存成功");
        }

        /// <summary>
        /// 加载并显示所有超级报文.
        /// </summary>
        private void 加载超级报文()
        {
            // 清除当前界面上所有的超级报文控件
            flowLayoutPanel_SuperMsg.Controls.Clear();

            // 清除所有超级报文的数据
            foreach (object obj in SuperMsgList)
            {
                ((List<SuperMsg.ParamClass_t>)obj).Clear();
            }
            SuperMsgList.Clear(); // 清空超级报文列表

            try
            {
                // 读取超级报文的指令个数
                int cnt = Convert.ToInt32(gCfg.IniRead("SuperMsg", "指令个数", gCfg.TxListPath));
                int param_num; // 参数数量变量

                // 循环加载每个超级报文
                for (int i = 0; i < cnt; i++)
                {
                    SuperMsg superMsg = new SuperMsg(); // 创建新的超级报文对象

                    // 读取当前指令的参数个数
                    param_num = Convert.ToInt32(gCfg.IniRead("SuperMsg", "指令" + i.ToString(), gCfg.TxListPath));

                    // 循环加载每个参数
                    for (int pos = 0; pos < param_num; pos++)
                    {
                        // 读取参数的保存信息
                        string save_info = gCfg.IniRead("SuperMsg",
                            "指令" + i.ToString() + '-' + pos.ToString(),
                            gCfg.TxListPath);

                        // 注册参数信息
                        superMsg.SuperMsgListParamReg(save_info);
                    }

                    // 创建超级报文的显示界面
                    superMsg.SuperMsgCreate(this);

                    // 读取并配置超级报文的发送配置
                    superMsg.SuperMsgTxCfg(gCfg.IniRead("SuperMsg", "指令" + i.ToString() + "报文", gCfg.TxListPath));

                    // 将超级报文控件添加到界面中
                    flowLayoutPanel_SuperMsg.Controls.Add(superMsg.panelMsg);
                }
            }
            catch
            {
                // 捕获异常但不处理，确保加载过程不会中断
            }
        }

        /// <summary>
        /// 加载自动下发指令的规则并显示在文本框中.
        /// </summary>
        private void 加载自动下发指令的规则()
        {
            try
            {
                // 从配置文件中读取自动下发指令的规则
                string data = gCfg.IniRead("自动下发指令", "规则", gCfg.TxListPath);

                // 替换换行符格式并设置到文本框中
                textBox_AutoTx.Text = data.Replace("\\n", "\r\n");

                // 更新自动下发指令的状态或配置
                toolStripButton_AutoTx_Update_Click(null, null);
            }
            catch
            {
                // 捕获异常但不处理，确保加载过程不会中断
            }
        }


        /// <summary>
        /// 加载简单的发送列表，从配置文件读取并创建相应的按钮.
        /// </summary>
        private void 加载简单的发送列表()
        {
            // 清除所有现有的数据和按钮
            txListSimple_List.Clear();  // 清空发送列表
            buttonListSimple.Clear();    // 清空按钮列表
            splitContainer_TxListSimple.Panel2.Controls.Clear(); // 清空面板中的控件
            buttonListSimpleNumber = 0;  // 重置按钮计数器

            try
            {
                // 从配置文件读取按钮数量
                int TxSimpleNum = Convert.ToInt32(gCfg.IniRead("TxListSimple", "按钮个数", gCfg.TxListPath));

                // 循环读取每个简单发送项
                for (int i = 0; i < TxSimpleNum; i++)
                {
                    TxListSimple txListSimple = new TxListSimple(); // 创建新发送项实例

                    // 从配置文件读取每个发送项的数据
                    string data = gCfg.IniRead("TxListSimple", "TX" + i.ToString(), gCfg.TxListPath);
                    string[] str = data.Split('$'); // 使用'$'分割数据

                    // 解析数据类型和消息名称
                    txListSimple.DataType = Convert.ToInt32(str[0]);
                    txListSimple.MsgName = str[1];

                    // 处理消息数据中的特殊字符
                    str[2] = str[2].Replace("\\\\&", "$");
                    string[] msg_data = str[2].Split(new string[] { "\\n" }, StringSplitOptions.RemoveEmptyEntries);
                    txListSimple.Msg = msg_data; // 设置消息内容

                    // 添加到发送列表
                    txListSimple_List.Add(txListSimple);
                }
            }
            catch
            {
                // 捕获异常，但不处理，确保程序稳定性
            }

            // 生成按钮
            if (txListSimple_List.Count > 0)
            {
                int last = txListSimple_List.Count - 1; // 获取最后一个索引

                // 为每个简单发送项创建按钮
                for (int i = 0; i < txListSimple_List.Count; i++)
                {
                    buttonTxListSimpleCreate(txListSimple_List[last - i].MsgName); // 创建按钮
                }
                txListSimple_DataLoad(0); // 加载第一个发送项的数据
            }
            else
            {
                // 如果没有发送项，创建一个默认的发送项
                TxListSimpleCreate(0);
            }

            // 额外代码用于处理界面显示状态（注释掉的部分）
            // if (tabControl_TxList.SelectedTab == tabPage_TxList)
            //     splitContainer_TxListSimple.Panel1Collapsed = true;
        }

        /// <summary>
        /// 加载应用程序的设定，包括串口配置、界面大小、按钮状态等.
        /// </summary>
        private void 加载设定()
        {
            // 检查配置文件是否存在
            if (gCfg.SysCfgFileExists())
            {
                string[] val = new string[2]; // 用于存储按钮的文本和颜色
                try
                {
                    // 从配置文件读取基本配置
                    toolStripComboBox_SerialPort.Text = gCfg.SysCfgFileRead("基本配置", "端口");
                    toolStripComboBox_BaudRate.Text = gCfg.SysCfgFileRead("基本配置", "波特率/IP");
                    toolStripTextBox_TCPPort.Text = gCfg.SysCfgFileRead("基本配置", "TCP端口");

                    // 设置窗口大小
                    //string[] size = gCfg.SysCfgFileRead("基本配置", "Form1Size").Split(',');
                    //this.Size = new Size(Convert.ToInt32(size[0]), Convert.ToInt32(size[1]));

                    // 获取屏幕的工作区域大小
                    Rectangle workingArea = Screen.GetWorkingArea(this);

                    // 设置窗口大小为屏幕工作区域的80%
                    this.Size = new Size((int)(workingArea.Width * 0.6), (int)(workingArea.Height * 0.6));


                    // 配置面板显示状态
                    splitContainer_Master.Panel2Collapsed = Convert.ToBoolean(gCfg.SysCfgFileRead("基本配置", "TableTx显隐"));
                    splitContainer_Master.SplitterDistance = Convert.ToInt32(gCfg.SysCfgFileRead("基本配置", "MasterSize"));
                    splitContainer1.SplitterDistance = Convert.ToInt32(gCfg.SysCfgFileRead("基本配置", "TableTxSize"));

                    // 读取并设置按钮的文本和背景颜色
                    val = gCfg.SysCfgFileRead("基本配置", "ButtonTxHEX").Split(',');
                    toolStripButton_TxHEX.Text = val[0];
                    toolStripButton_TxHEX.BackColor = Color.FromName(val[1]);
                    val = gCfg.SysCfgFileRead("基本配置", "ButtonRxHEX").Split(',');
                    toolStripButton_RxHEX.Text = val[0];
                    toolStripButton_RxHEX.BackColor = Color.FromName(val[1]);
                    val = gCfg.SysCfgFileRead("基本配置", "ButtonRxAutoNewline").Split(',');
                    toolStripButton_RxAutoNewline.Text = val[0];
                    toolStripButton_RxAutoNewline.BackColor = Color.FromName(val[1]);
                    val = gCfg.SysCfgFileRead("基本配置", "ButtonTimestamp").Split(',');
                    toolStripButton_Timestamp.Text = val[0];
                    toolStripButton_Timestamp.BackColor = Color.FromName(val[1]);

                    // 设置回显文本颜色
                    toolStripLabel1.ForeColor = Color.FromName(gCfg.SysCfgFileRead("基本配置", "Tx回显"));
                    if (toolStripLabel1.ForeColor == Color.Red)
                        gTxShowFlag = true; // 设置标志

                    // 读取主设备的报尾
                    MasterMsgEnd.Text = gCfg.SysCfgFileRead("基本配置", "主设备的报尾");

                    // 设置自动换行的定时器周期
                    string RxAutoNewDelay = gCfg.SysCfgFileRead("基本配置", "自动换行时间");
                    if (RxAutoNewDelay == "")
                        RxAutoNewDelay = TimerRxAutoNewlinePeriod.ToString();
                    toolStripTextBox自动换行定时器周期.Text = RxAutoNewDelay;
                    Timer_RxAutoNewLine_Init(Convert.ToInt32(RxAutoNewDelay));

                    // 读取发送区文本
                    TextBox_Tx.Text = gCfg.SysCfgFileRead("发送区", "MasterTx");
                    richTextBox_Rx.Font = new Font(richTextBox_Rx.Font.FontFamily,
                        Convert.ToSingle(gCfg.SysCfgFileRead("接收区", "RxShowTextSize")),
                        richTextBox_Rx.Font.Style);

                    // 加载发送列表的路径
                    string path = gCfg.SysCfgFileRead("发送列表", "path");
                    if ("" != path)
                    {
                        gCfg.TxListPath = path;
                        gCfg.TxListNum = gCfg.TxList_GetNum(path); // 获取发送列表的数量
                    }
                    toolStripTextBox_TxListNum.Text = gCfg.TxListNum.ToString(); // 显示数量

                    加载超级报文(); // 加载超级报文配置
                }
                catch
                {
                    // 捕获异常，保持程序稳定
                }
            }

            // 加载精简发送列表
            加载简单的发送列表();

            // 加载自动下发指令的规则
            加载自动下发指令的规则();
        }


        /// <summary>
        /// 处理在接收文本框中点击链接时的事件.
        /// </summary>
        /// <param name="sender">触发事件的对象.</param>
        /// <param name="e">链接点击事件的参数.</param>
        private void richTextBox_Rx_LinkClicked(object sender, LinkClickedEventArgs e)
        {
            // 使用系统默认浏览器打开链接
            System.Diagnostics.Process.Start(e.LinkText);
        }

        /// <summary>
        /// 处理接收文本框内容调整大小时的事件.
        /// </summary>
        /// <param name="sender">触发事件的对象.</param>
        /// <param name="e">内容调整大小事件的参数.</param>
        private void richTextBox_Rx_ContentsResized(object sender, ContentsResizedEventArgs e)
        {
            // 确保滚动条滚动到当前光标位置
            richTextBox_Rx.ScrollToCaret();
        }

        /// <summary>
        /// 切换工具条按钮的文本和背景颜色，切换显示模式.
        /// </summary>
        /// <param name="sender">触发事件的对象.</param>
        /// <param name="e">事件参数.</param>
        private void toolStripButton_HEX_Click(object sender, EventArgs e)
        {
            // 将触发事件的对象转换为 ToolStripButton 类型
            ToolStripButton ylbtt = (ToolStripButton)sender;

            // 判断当前按钮的文本，如果是 "ASCII"，则切换为 "HEX"
            if (ylbtt.Text == "ASCII")
            {
                ylbtt.Text = "HEX"; // 更新按钮文本
                ylbtt.BackColor = Color.LimeGreen; // 设置按钮背景为绿色
            }
            else
            {
                ylbtt.Text = "ASCII"; // 切换回 "ASCII"
                ylbtt.BackColor = Color.Transparent; // 恢复按钮背景为透明
            }
        }

        /// <summary>
        /// 切换自动换行功能的状态.
        /// </summary>
        /// <param name="sender">触发事件的对象.</param>
        /// <param name="e">鼠标事件参数.</param>
        private void toolStripButton_RxAutoNewline_MouseDown(object sender, MouseEventArgs e)
        {
            // 检查当前按钮的文本，以确定功能状态
            if (toolStripButton_RxAutoNewline.Text == "OFF")
            {
                toolStripButton_RxAutoNewline.Text = "ON"; // 设置为 "ON"，表示自动换行功能开启
                toolStripButton_RxAutoNewline.BackColor = Color.LimeGreen; // 改变按钮背景色为绿色，表示开启状态
            }
            else
            {
                toolStripButton_RxAutoNewline.Text = "OFF"; // 设置为 "OFF"，表示自动换行功能关闭
                toolStripButton_RxAutoNewline.BackColor = Color.Transparent; // 恢复按钮背景色为透明，表示关闭状态
            }
        }

        /// <summary>
        /// 切换时间戳功能的状态，并相应地管理自动换行功能.
        /// </summary>
        /// <param name="sender">触发事件的对象.</param>
        /// <param name="e">鼠标事件参数.</param>
        private void toolStripButton_Timestamp_MouseDown(object sender, MouseEventArgs e)
        {
            // 检查当前按钮的文本，以确定时间戳功能状态
            if (toolStripButton_Timestamp.Text == "OFF")
            {
                toolStripButton_Timestamp.Text = "ON"; // 设置为 "ON"，表示时间戳功能开启
                toolStripButton_Timestamp.BackColor = Color.LimeGreen; // 改变按钮背景色为绿色，表示开启状态

                // 同时开启自动换行功能
                toolStripButton_RxAutoNewline.Text = "ON"; // 设置自动换行状态为 "ON"
                toolStripButton_RxAutoNewline.BackColor = Color.LimeGreen; // 改变自动换行按钮背景色为绿色
                toolStripButton_RxAutoNewline.Enabled = false; // 禁用自动换行按钮，防止重复操作
            }
            else
            {
                toolStripButton_Timestamp.Text = "OFF"; // 设置为 "OFF"，表示时间戳功能关闭
                toolStripButton_Timestamp.BackColor = Color.Transparent; // 恢复按钮背景色为透明，表示关闭状态

                // 同时关闭自动换行功能
                toolStripButton_RxAutoNewline.Text = "OFF"; // 设置自动换行状态为 "OFF"
                toolStripButton_RxAutoNewline.BackColor = Color.Transparent; // 恢复自动换行按钮背景色为透明
                toolStripButton_RxAutoNewline.Enabled = true; // 启用自动换行按钮
            }
        }


        /// <summary>
        /// 切换主面板的折叠状态，控制面板的可见性.
        /// </summary>
        /// <param name="sender">触发事件的对象.</param>
        /// <param name="e">鼠标事件参数.</param>
        private void toolStripButton_Fold_MouseDown(object sender, MouseEventArgs e)
        {
            // 检查当前主面板的第二个面板是否已折叠
            if (splitContainer_Master.Panel2Collapsed == true)
                splitContainer_Master.Panel2Collapsed = false; // 如果折叠，展开面板
            else
                splitContainer_Master.Panel2Collapsed = true; // 否则，折叠面板
        }


        /// <summary>
        /// 切换窗口的置顶状态.
        /// </summary>
        /// <param name="sender">触发事件的对象.</param>
        /// <param name="e">鼠标事件参数.</param>
        private void toolStripButton_OnTop_MouseDown(object sender, MouseEventArgs e)
        {
            // 检查当前按钮文本，以确定当前状态
            if (toolStripButton_OnTop.Text == "置顶")
            {
                // 设置按钮为“取消置顶”，并改变背景颜色
                toolStripButton_OnTop.BackColor = Color.Tomato;
                toolStripButton_OnTop.Text = "取消置顶";
                this.TopMost = true; // 将窗口设置为置顶
            }
            else
            {
                // 恢复按钮为“置顶”，并重置背景颜色
                toolStripButton_OnTop.BackColor = Color.Transparent;
                toolStripButton_OnTop.Text = "置顶";
                this.TopMost = false; // 取消置顶状态
            }
        }


        /// <summary>
        /// 在接收区域和十六进制显示区域中添加文本，并设置指定颜色.
        /// </summary>
        /// <param name="c">文本的颜色.</param>
        /// <param name="text">要显示的文本.</param>
        private void HelpShow(Color c, string text)
        {
            // 在接收区添加新行
            richTextBox_Rx.AppendText(System.Environment.NewLine);
            // 设置接收区文本的颜色
            richTextBox_Rx.SelectionColor = c;
            // 在接收区添加指定文本
            richTextBox_Rx.AppendText(text);

            // 在十六进制显示区添加新行
            richTextBox_HexShow.AppendText(System.Environment.NewLine);
            // 设置十六进制显示区文本的颜色
            richTextBox_HexShow.SelectionColor = c;
            // 在十六进制显示区添加指定文本
            richTextBox_HexShow.AppendText(text);
        }

        /// <summary>
        /// 在接收区域和十六进制显示区域中添加新行.
        /// </summary>
        private void HelpShowNewLine()
        {
            // 在接收区添加新行
            richTextBox_Rx.AppendText(System.Environment.NewLine);
            // 在十六进制显示区添加新行
            richTextBox_HexShow.AppendText(System.Environment.NewLine);
        }

        /// <summary>
        /// 处理帮助按钮的鼠标按下事件，显示应用程序的帮助信息。
        /// </summary>
        private void toolStripButton_help_MouseDown(object sender, MouseEventArgs e)
        {
            richTextBox_Rx.Text = "";
            richTextBox_HexShow.Text = "";
            HelpShow(Color.Gold, "串口助手V2.1：");
            HelpShow(Color.Lime, "公众号：碎片聚合");
            HelpShow(Color.Lime, "GitHub：https://github.com/L231");
            HelpShow(Color.Lime, "下载链接 https://pan.baidu.com/s/1uoS-Xpm8JSPdkq5KYSMFFA?pwd=g1di");
            HelpShowNewLine();
            HelpShowNewLine();

            HelpShow(Color.Gold, "超级报文：");
            HelpShowNewLine();
            HelpShow(Color.Gold, "1.概述");
            HelpShow(Color.Moccasin, "1.1 简介");
            HelpShow(Color.Moccasin, "    使用主通信端口，在HEX格式下，提取报文中的特殊参数，进行监控");
            HelpShow(Color.Moccasin, "1.2 结构");
            HelpShow(Color.Moccasin, "    左侧快速控制栏 + 顶部显示区 + 报文列表 + 右键系统");
            HelpShow(Color.Moccasin, "1.3 每条报文的结构");
            HelpShow(Color.Moccasin, "    删除键 + 命名框 + 接收解析框 + 分割线 + 发送复选框 + 发送键");
            HelpShowNewLine();
            HelpShow(Color.Gold, "2.用法");
            HelpShow(Color.Moccasin, "2.1 创建新报文");
            HelpShow(Color.Moccasin, "    右键空白区，设置发送、接收框个数后创建");
            HelpShow(Color.Moccasin, "2.2 配置发送、接收框");
            HelpShow(Color.Moccasin, "    右键文本框，按自身需求配置相关参数，最后完成配置");
            HelpShow(Color.Moccasin, "2.3 设置报文");
            HelpShow(Color.Moccasin, "    右键发送键，输入文本即可");
            HelpShow(Color.Moccasin, "2.4 发送");
            HelpShow(Color.Moccasin, "    连接主通信端口后，单击发送键，发出报文");
            HelpShowNewLine();
            HelpShowNewLine();

            HelpShow(Color.Gold, "多级发送列表：");
            HelpShowNewLine();
            HelpShow(Color.Gold, "1.结构");
            HelpShow(Color.Moccasin, "监控区：自动按钮 + 发送状态");
            HelpShow(Color.Moccasin, "发送列表：HEX复选框 + 延时时间 + 数据框 + 按钮");
            HelpShowNewLine();
            HelpShow(Color.Gold, "2.发送按钮命名");
            HelpShow(Color.Moccasin, "2.1 右键发送按钮，完成命名");
            HelpShow(Color.Moccasin, "2.2 解锁情况下，双击“数据框”，可将文本移到按钮");
            HelpShowNewLine();
            HelpShow(Color.Gold, "3.循环发送");
            HelpShow(Color.Moccasin, "“自动”键开启，未设置延时时间的，自动跳过");
            HelpShowNewLine();
            HelpShow(Color.Gold, "4.报文打包（数据框可多行编辑）");
            HelpShow(Color.Moccasin, "4.1 结构：报文行、指令行，二者要区分开");
            HelpShow(Color.Moccasin, "4.2 指令(不区分大小写)：");
            HelpShow(Color.Moccasin, "    loop ... loopXX");
            HelpShow(Color.Moccasin, "    delayXX");
            HelpShow(Color.Moccasin, "    delayXX,loopxx");
            HelpShow(Color.Moccasin, "4.3 举例，数据框写入下述内容：");
            HelpShow(Color.Gold, "    loop");
            HelpShow(Color.Gold, "    12 25");
            HelpShow(Color.Gold, "    delay100");
            HelpShow(Color.Gold, "    12 34 56 78");
            HelpShow(Color.Gold, "    delay100,loop2");
            HelpShow(Color.Gold, "    loop3");
            HelpShow(Color.Moccasin, "4.4 说明：");
            HelpShow(Color.Moccasin, "    delay100，表示延时100ms");
            HelpShow(Color.Moccasin, "    delay100,loop2，表示上条报文发送2次，每次延时100ms");
            HelpShow(Color.Moccasin, "    loop 与 loop3，构成一个循环体，循环3次。可嵌套循环");
            HelpShowNewLine();
            HelpShow(Color.Gold, "5.多设备通信（共享数据收发区，报文也能打包）");
            HelpShow(Color.Moccasin, "5.1 注意 HEX按钮、报文结束符的配置");
            HelpShow(Color.Moccasin, "5.2 用法：设备名$ + 报文（只需在待发送数据前加设备前缀）");
            HelpShow(Color.Moccasin, "5.3 [设备名]是唯一的，注册时用户自定义");
            HelpShow(Color.Moccasin, "5.4 举例：万用表$READ? =>获取万用表读值");
            HelpShowNewLine();
            HelpShowNewLine();

            HelpShow(Color.Gold, "bootloader：");
            HelpShowNewLine();
            HelpShow(Color.Gold, "1.bootloader报文结构");
            HelpShow(Color.Moccasin, "采用checksum，报文所有字节相加，取反做报尾cs");
            HelpShow(Color.Orange, "[cmd] ..... [cs]");
            HelpShowNewLine();
            HelpShow(Color.Gold, "2.协议定义");
            HelpShow(Color.Gold, "2.1 进入bootloader");
            HelpShow(Color.Orange, "-> 5B AB [mcu] [BRG] [cs]");
            HelpShow(Color.Orange, "<- 无应答");
            HelpShow(Color.Moccasin, "[mcu]指示下位机中哪个mcu进入(可不使用)。");
            HelpShow(Color.Moccasin, "[BRG]配置主控MCU与[mcu]的通信速率(可不使用)。");
            HelpShowNewLine();

            HelpShow(Color.Gold, "2.2 波特率自动检测帧");
            HelpShow(Color.Orange, "-> 55");
            HelpShow(Color.Moccasin, "PC发送“进入bootloader”指令后，自动下发");
            HelpShowNewLine();

            HelpShow(Color.Gold, "2.3 跳转APP");
            HelpShow(Color.Orange, "-> 5A 5A A5 A6");
            HelpShow(Color.Orange, "<- 无应答");
            HelpShowNewLine();

            HelpShow(Color.Gold, "2.4 建立连接");
            HelpShow(Color.Orange, "-> AA [cs]");
            HelpShow(Color.Orange, "<- AA 00 00 00 00 [sizeH] [sizeL] [packH] [packL] [mcu] [bit] [cs]");
            HelpShow(Color.Moccasin, "[size]MCU剩余的APP区域大小(KB)。");
            HelpShow(Color.Moccasin, "[pack]MCU的Flash页大小(字节)。");
            HelpShow(Color.Moccasin, "[mcu]MCU编号。");
            HelpShow(Color.Moccasin, "[bit]MCU位数，0：8bit，1：16bit，2：32bit.");
            HelpShowNewLine();

            HelpShow(Color.Gold, "2.5 烧录Flash");
            HelpShow(Color.Orange, "-> A1 [addrHH] [addrHL] [addrLH] [addrLL] [lenH] [lenL] [D0] ... [Dx] [cs]");
            HelpShow(Color.Orange, "<- A1 [status] [cs]");
            HelpShow(Color.Moccasin, "[addr]4字节的烧录地址。");
            HelpShow(Color.Moccasin, "[len]烧录数据的长度，固定为[pack]字节。");
            HelpShow(Color.Moccasin, "[D]烧录数据。");
            HelpShow(Color.Moccasin, "[status]状态，00：成功，非零：异常");
            HelpShowNewLine();

            HelpShow(Color.Gold, "2.6 校验");
            HelpShow(Color.Orange, "-> A2 [addrHH] [addrHL] [addrLH] [addrLL] [lenH] [lenL] [cs]");
            HelpShow(Color.Orange, "<- A2 [status] [D0] ... [Dx] [cs]");
            HelpShow(Color.Moccasin, "[addr]4字节的烧录地址。");
            HelpShow(Color.Moccasin, "[len]烧录数据的长度，固定为[pack]字节。");
            HelpShow(Color.Moccasin, "[status]状态，00：正常，非零：地址、cs校验异常");
            HelpShow(Color.Moccasin, "[D]从Flash读出的数据，个数为[pack]字节。");
            HelpShowNewLine();

            HelpShow(Color.Gold, "2.7 全擦");
            HelpShow(Color.Orange, "-> A3 00 00 00 00 [cs]");
            HelpShow(Color.Orange, "<- A3 [status] [cs]");
            HelpShow(Color.Moccasin, "[status]状态，00：正常，非零：擦除失败");
            HelpShowNewLine();
            toolStripButton_RecClear.Enabled = true;
            toolStripButton_RecClear.BackColor = Color.Gold;

            HelpShowNewLine();
            HelpShowNewLine();
            HelpShow(Color.Gold, "接收显示区右键，解锁控件后输入：help，回车");
            HelpShowNewLine();
        }

        /// <summary>
        /// 处理 TextBox 控件的 KeyUp 事件，当用户按下 Enter 键且行数不超过 2 时，调整相关界面元素。
        /// </summary>
        private void TextboxTX_KeyUp(object sender, KeyEventArgs e)
        {
            // 将发送者转换为 TextBox 类型
            TextBox yltb = sender as TextBox;

            // 检查按下的键是否为 Enter，且 TextBox 中的行数不超过 2
            if (e.KeyCode == Keys.Enter && yltb.Lines.Length <= 2)
            {
                try
                {
                    int i = 0;
                    // 获取当前 TextBox 的滚动位置
                    int val = panel3.VerticalScroll.Value;

                    // 查找当前 TextBox 在配置列表中的索引
                    for (; i < gCfg.TxListNum; i++)
                    {
                        if (yltb == gCfg.TextboxTX[i])
                            break;
                    }

                    // 如果找不到，使用默认索引
                    if (i >= gCfg.TxListNum)
                        i = gCfg.TxListNum - 1;

                    // 移除 LostFocus 事件处理程序，避免在调整大小时触发其他事件
                    yltb.LostFocus -= new EventHandler(TextboxTX_LostFocusClick);

                    // 设置 TextBox 的滚动条为垂直
                    yltb.ScrollBars = ScrollBars.Vertical;

                    // 调整当前行的高度
                    tableLayoutPanel1.RowStyles[i].Height = 15 * 10;

                    // 调整表格布局面板的大小
                    tableLayoutPanel1.Size = new Size(tableLayoutPanel1.Size.Width, (20 * (gCfg.TxListNum - 1)) + 15 * 10);

                    // 重新添加 LostFocus 事件处理程序
                    yltb.LostFocus += new EventHandler(TextboxTX_LostFocusClick);

                    // 恢复之前的滚动位置
                    panel3.VerticalScroll.Value = val;
                }
                catch
                {
                    // 捕获异常并显示错误信息
                    MessageBox.Show("回车异常！", "ERROR");
                }
            }
        }

        /// <summary>
        /// 处理 TextBox 控件的 GotFocus 事件，调整界面元素以适应用户输入。
        /// </summary>
        private void TextboxTX_GotFocusClick(object sender, EventArgs e)
        {
            int i = 0;
            try
            {
                // 将发送者转换为 TextBox 类型
                TextBox yltb = sender as TextBox;

                // 如果 TextBox 的行数少于 2，则不执行任何操作
                if (yltb.Lines.Length < 2)
                    return;

                // 查找当前 TextBox 在配置列表中的索引
                for (; i < gCfg.TxListNum; i++)
                {
                    if (yltb == gCfg.TextboxTX[i])
                        break;
                }

                // 如果找不到，使用默认索引
                if (i >= gCfg.TxListNum)
                    i = gCfg.TxListNum - 1;

                // 移除 GotFocus 事件处理程序，以避免递归调用
                yltb.GotFocus -= new EventHandler(TextboxTX_GotFocusClick);

                // 设置行高度
                int length = 10; // 行高的基准值

                // 获取当前的垂直滚动位置
                int val = panel3.VerticalScroll.Value;

                // 设置 TextBox 的滚动条为垂直
                yltb.ScrollBars = ScrollBars.Vertical;

                // 恢复之前的滚动位置
                panel3.VerticalScroll.Value = val;

                // 调整当前行的高度
                tableLayoutPanel1.RowStyles[i].Height = 15 * length;

                // 调整表格布局面板的大小
                tableLayoutPanel1.Size = new Size(tableLayoutPanel1.Size.Width, (20 * (gCfg.TxListNum - 1)) + 15 * length);

                // 重新添加 GotFocus 事件处理程序
                yltb.GotFocus += new EventHandler(TextboxTX_GotFocusClick);
            }
            catch
            {
                // 捕获异常并显示错误信息
                MessageBox.Show("进入异常！", "ERROR");
            }
        }

        /// <summary>
        /// 处理 TextBox 控件的 LostFocus 事件，恢复界面元素的状态。
        /// </summary>
        private void TextboxTX_LostFocusClick(object sender, EventArgs e)
        {
            int i = 0;
            try
            {
                // 将发送者转换为 TextBox 类型
                TextBox yltb = sender as TextBox;

                // 查找当前 TextBox 在配置列表中的索引
                for (; i < gCfg.TxListNum; i++)
                {
                    if (yltb == gCfg.TextboxTX[i])
                        break;
                }

                // 如果找不到，使用默认索引
                if (i >= gCfg.TxListNum)
                    i = gCfg.TxListNum - 1;

                // 如果当前行的高度已经是 20，则不进行任何操作
                if (tableLayoutPanel1.RowStyles[i].Height == 20)
                    return;

                // 移除 LostFocus 事件处理程序，以避免递归调用
                yltb.LostFocus -= new EventHandler(TextboxTX_LostFocusClick);

                // 隐藏滚动条
                yltb.ScrollBars = ScrollBars.None;

                // 将当前行的高度重置为 20
                tableLayoutPanel1.RowStyles[i].Height = 20;

                // 调整表格布局面板的大小
                tableLayoutPanel1.Size = new Size(tableLayoutPanel1.Size.Width, (20 * gCfg.TxListNum));

                // 重新添加 GotFocus 事件处理程序
                yltb.GotFocus += new EventHandler(TextboxTX_GotFocusClick);
            }
            catch
            {
                // 捕获异常并显示错误信息
                MessageBox.Show("退出异常！", "ERROR");
            }
        }

        /// <summary>
        /// 获取循环计数，如果数据中包含"LOOP"指令，则解析循环次数。
        /// </summary>
        /// <param name="data">输入的字符串数据。</param>
        /// <returns>循环计数；若未找到或发生错误，则返回 -1。</returns>
        private int GetTxLoopCnt(string data)
        {
            try
            {
                // 检查输入数据中是否包含"LOOP"（不区分大小写）
                if (data.IndexOf("LooP", StringComparison.OrdinalIgnoreCase) >= 0)
                {
                    // 如果只包含"LOOP"，返回 0（表示循环 0 次）
                    if (data.Replace(" ", "").Length == 4)
                        return 0;

                    // 创建数据类型转换实例
                    DataTypeConversion dataType = new DataTypeConversion();

                    // 解析并返回循环次数
                    return dataType.GetStringNumber(data);
                }
            }
            catch
            {
                // 捕获异常，不执行任何操作
            }

            // 默认返回 -1，表示未找到有效的循环计数
            return -1;
        }

        /// <summary>
        /// 创建一个指定毫秒数的延迟.
        /// </summary>
        /// <param name="tick">延迟的时间（以毫秒为单位）.</param>
        private void Delay(int tick)
        {
            // 创建一个 Stopwatch 实例以测量时间
            System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();

            // 启动 Stopwatch
            stopwatch.Start();

            // 循环直到经过的时间达到指定的延迟
            while (stopwatch.Elapsed.TotalMilliseconds < tick)
            {
                // 每次循环等待 1 毫秒
                Thread.Sleep(1);
            }

            // 停止 Stopwatch
            stopwatch.Stop();
        }

        /// <summary>
        /// 处理和发送多个消息，支持延迟和循环功能.
        /// </summary>
        /// <param name="msg">消息数组.</param>
        /// <param name="posStart">开始处理的消息索引.</param>
        /// <param name="dataType">消息的数据类型.</param>
        /// <returns>处理后的位置索引.</returns>
        private int CommMultipleMsgSend(string[] msg, int posStart, string dataType)
        {
            int posEnd = -1; // 循环结束的位置
            int posLast = posStart; // 上一条发送的消息的位置
            int loop = -1; // 循环计数
            int cnt = 0; // 循环计数器

            // 遍历消息数组
            for (int line = posStart; line < msg.Length; line++)
            {
                if (msg[line] == "")
                    continue; // 跳过空消息

                // 如果循环次数用完了，返回结束位置
                if (loop == 0)
                    return posEnd;

                // 如果达到了循环结束的位置
                if (posEnd == line)
                {
                    loop--; // 减少循环计数
                    line = posStart - 1; // 重新开始处理
                    continue;
                }

                // 延时处理和单条报文循环
                if (msg[line].IndexOf("DelaY", StringComparison.OrdinalIgnoreCase) >= 0)
                {
                    DataTypeConversion Conversion = new DataTypeConversion();
                    int delay = Conversion.GetStringNumber(msg[line]);
                    int sLoop = 0;
                    msg[line] = msg[line].ToUpper(); // 转换为大写以进行匹配

                    // 检查是否包含循环标识
                    if (msg[line].Contains("LOOP"))
                    {
                        string[] delay_loop = msg[line].Split(new string[] { "LOOP" }, StringSplitOptions.RemoveEmptyEntries);
                        delay = Conversion.GetStringNumber(delay_loop[0]); // 获取延迟时间
                        if (delay_loop.Length > 1)
                            sLoop = Conversion.GetStringNumber(delay_loop[1]) - 1; // 获取循环次数
                    }

                    // 执行延时和发送消息
                    while (sLoop-- > 0)
                    {
                        Delay(delay); // 执行延迟
                        CommSingleMsgSend(msg[posLast], dataType); // 发送上一条消息
                    }
                    Delay(delay); // 最后一次延迟
                    continue;
                }

                // 获取消息行中的循环计数
                cnt = GetTxLoopCnt(msg[line]);

                // 如果遇到嵌套的循环体
                if (cnt == 0)
                {
                    line = CommMultipleMsgSend(msg, line + 1, dataType); // 递归处理嵌套
                    continue;
                }
                // 如果遇到循环体的结束语句
                else if (cnt > 0)
                {
                    // 检查结束语句是否合法
                    if (posStart > 0 && GetTxLoopCnt(msg[posStart - 1]) == 0)
                    {
                        posEnd = line; // 设置结束位置
                        loop = cnt - 1; // 减少循环计数
                        line = posStart - 1; // 重新开始处理
                    }
                    continue;
                }

                // 发送当前消息
                CommSingleMsgSend(msg[line], dataType);
                posLast = line; // 更新最后发送的消息位置
            }
            return msg.Length; // 返回消息长度
        }


        /// <summary>
        /// 处理按钮点击事件，发送指定格式的消息。
        /// </summary>
        /// <param name="flag_hex">指示消息是否应该以 HEX 格式发送。</param>
        /// <param name="txmsg">要发送的消息数组。</param>
        public void buttonClick(bool flag_hex, string[] txmsg)
        {
            // 检查主通信类型是否已设置
            if (masterComm.type != "")
            {
                // 如果清除按钮当前未启用，则启用它
                if (toolStripButton_RecClear.Enabled == false)
                {
                    toolStripButton_RecClear.Enabled = true;
                    toolStripButton_RecClear.BackColor = Color.Gold; // 更改按钮颜色以表示已启用
                }

                string temp = "ASCII"; // 默认消息格式
                string pattern = @"[^0-9]+"; // 匹配非数字字符的模式
                Regex rgx = new Regex(pattern); // 用模式初始化正则表达式

                // 检查消息是否应该以 HEX 格式发送
                if (flag_hex)
                {
                    temp = "HEX"; // 如果标志为真，则将格式更新为 HEX
                }

                // 根据 txmsg 中的条目数量发送消息
                if (txmsg.Length == 1)
                {
                    // 如果只有一条消息，直接发送
                    CommSingleMsgSend(txmsg[0], temp);
                }
                else
                {
                    // 如果有多条消息，则发送所有消息
                    CommMultipleMsgSend(txmsg, 0, temp);
                }
            }
        }

        private void TableTxSingle_Click(object pos)
        {
            bool flagHEX = false; // 默认设置为非HEX格式
            string[] TxMsg = null; // 用于存储要发送的消息
            int i = (int)pos; // 将传入的位置转换为整数

            // 检查当前选中的标签页
            if (tabControl_TxList.SelectedTab == tabPage_TxList)
            {
                // 从配置中获取选中的复选框状态和消息行
                flagHEX = gCfg.Checkbox[i].Checked; // 检查是否选中HEX复选框
                TxMsg = gCfg.TextboxTX[i].Lines; // 获取要发送的消息行
            }
            else
            {
                // 对于其他标签页，根据数据类型确定发送格式
                if (txListSimple_List[i].DataType == 0)
                    flagHEX = true; // 如果数据类型为0，设置为HEX格式
                TxMsg = txListSimple_List[i].Msg; // 获取要发送的消息
            }

            // 调用按钮点击方法发送消息
            buttonClick(flagHEX, TxMsg);

            // 设置标志位和停止线程
            threadTableTx_RunFlag = false;
            threadTableTx.Abort(); // 中止当前线程
        }

        /// <summary>
        /// 处理按钮点击事件，发送消息并管理线程状态。
        /// </summary>
        /// <param name="sender">触发事件的按钮。</param>
        /// <param name="e">事件数据。</param>
        private void button_Click(object sender, EventArgs e)
        {
            // 检查是否已有线程在运行
            if (threadTableTx_RunFlag)
                return; // 如果已有线程在运行，则直接返回

            int i = 0; // 初始化索引
            Button ylbtt = sender as Button; // 获取被点击的按钮

            // 检查当前选中的标签页
            if (tabControl_TxList.SelectedTab == tabPage_TxList)
            {
                // 在配置中查找对应的按钮索引
                for (; i < gCfg.TxListNum; i++)
                {
                    if (ylbtt == gCfg.Button[i])
                        break; // 找到匹配的按钮
                }
            }
            else
            {
                // 如果不是 tabPage_TxList，提取当前按钮的数据
                i = buttonListSimpleNumber_Get(ylbtt); // 获取按钮在简单列表中的索引
                txListSimple_DataLoad(i); // 加载相应数据
                if (txListSimple_List[i].Msg == null)
                    return; // 如果消息为空，直接返回
            }

            // 创建并启动新线程来处理消息发送
            threadTableTx = new Thread(TableTxSingle_Click);
            threadTableTx.IsBackground = true; // 设置为后台线程
            threadTableTx_RunFlag = true; // 标记线程正在运行
            threadTableTx.Start(i); // 启动线程并传递索引
        }


        /// <summary>
        /// 处理延时发送的逻辑，根据配置中的时间间隔发送消息。
        /// </summary>
        private void TableTx_Click()
        {
            toolStripLabel_TableTx.Text = "空闲，未设置延时发送"; // 初始化状态标签

            while (true) // 循环处理发送
            {
                // 检查是否有引导加载标志，若有则退出
                if (gBootloaderFlag)
                    goto THREAD_TABLE_TX_ABORT;

                // 遍历所有配置的发送项
                for (int i = 0; i < gCfg.TxListNum; i++)
                {
                    // 检查定时器文本框是否为空
                    if (gCfg.TextboxTimer[i].Text.Length == 0)
                        continue; // 如果为空，跳过当前循环

                    int delay = 0; // 初始化延迟变量
                    try
                    {
                        // 尝试将文本框中的值转换为整数
                        delay = Convert.ToInt32(gCfg.TextboxTimer[i].Text);
                    }
                    catch
                    {
                        // 转换失败，清空文本框并继续下一个循环
                        gCfg.TextboxTimer[i].Text = "";
                        continue;
                    }

                    try
                    {
                        // 更新状态标签，显示当前延时和按钮文本
                        toolStripLabel_TableTx.Text = "延时" + delay + "ms, TX: " + gCfg.Button[i].Text;
                        Delay(delay); // 调用延迟函数
                                      // 发送消息
                        buttonClick(gCfg.Checkbox[i].Checked, gCfg.TextboxTX[i].Lines);
                    }
                    catch
                    {
                        // 出现异常，退出循环
                        goto THREAD_TABLE_TX_ABORT;
                    }
                }
            }

        THREAD_TABLE_TX_ABORT:
            // 线程结束后的处理
            toolStripButton_TableTx.Text = "自动"; // 恢复按钮状态
            toolStripButton_TableTx.BackColor = Color.Lime; // 改变按钮背景色
            checkBox_TimSend.Enabled = true; // 启用定时发送复选框
            threadTableTx_RunFlag = false; // 标记线程状态为未运行
            threadTableTx.Abort(); // 中止当前线程
        }

        /// <summary>
        /// 处理表格发送按钮的点击事件，用于开启或关闭自动循环发送功能。
        /// </summary>
        /// <param name="sender">事件的发送者。</param>
        /// <param name="e">事件参数。</param>
        private void toolStripButton_TableTx_Click(object sender, EventArgs e)
        {
            // 判断是否未开启自动循环发送且已连接通信端口
            if (toolStripButton_TableTx.Text == "自动" && toolStripButton_Master.Text == "关闭" && gBootloaderFlag == false)
            {
                // 如果当前线程正在运行，直接返回
                if (threadTableTx_RunFlag)
                    return;

                // 停止定时器和禁用相关控件
                timer_Send.Stop();
                checkBox_TimSend.Checked = false;
                checkBox_TimSend.Enabled = false;

                // 更新按钮状态和背景颜色
                toolStripButton_TableTx.Text = "关闭";
                toolStripButton_TableTx.BackColor = Color.Tomato;

                // 创建并启动新的线程以处理延时发送
                threadTableTx = new Thread(TableTx_Click);
                threadTableTx.IsBackground = true; // 设置为后台线程
                threadTableTx_RunFlag = true; // 标记线程为运行状态
                threadTableTx.Start(); // 启动线程
            }
            else if (toolStripButton_TableTx.Text == "关闭")
            {
                // 更新状态标签
                toolStripLabel_TableTx.Text = "空闲";
                toolStripButton_TableTx.Text = "自动"; // 恢复按钮状态
                toolStripButton_TableTx.BackColor = Color.Lime; // 改变按钮背景色

                // 启用定时发送复选框
                checkBox_TimSend.Enabled = true;

                // 标记线程为未运行并中止当前线程
                threadTableTx_RunFlag = false;
                threadTableTx.Abort();
            }
        }


        /// <summary>
        /// 处理滑动条的鼠标移动事件，动态调整滑动条的大小。
        /// </summary>
        /// <param name="sender">事件的发送者。</param>
        /// <param name="e">鼠标事件参数，包含有关鼠标移动的信息。</param>
        private void trackBar1_MouseMove(object sender, MouseEventArgs e)
        {
            // 设置滑动条的大小为100x23
            trackBar1.Size = new Size(100, 23);
        }


        /// <summary>
        /// 处理滑动条的鼠标离开事件，调整滑动条的大小。
        /// </summary>
        /// <param name="sender">事件的发送者。</param>
        /// <param name="e">事件参数，包含有关鼠标离开的信息。</param>
        private void trackBar1_MouseLeave(object sender, EventArgs e)
        {
            // 将滑动条的大小调整为20x23
            trackBar1.Size = new Size(20, 23);
        }


        /// <summary>
        /// 处理滑动条滚动事件，调整窗口的透明度。
        /// </summary>
        /// <param name="sender">事件的发送者，通常是滑动条控件。</param>
        /// <param name="e">事件参数，包含有关滑动条滚动的信息。</param>
        private void trackBar1_Scroll(object sender, EventArgs e)
        {
            // 根据滑动条的当前值调整窗口的透明度
            // 滑动条的值范围是0到100，透明度范围是0.0到1.0
            this.Opacity = (100 - trackBar1.Value) / 100.0;
        }


        /// <summary>
        /// 处理工具条标签的点击事件，切换显示状态并更新时间戳按钮的状态。
        /// </summary>
        /// <param name="sender">事件的发送者，通常是工具条标签控件。</param>
        /// <param name="e">事件参数，包含有关鼠标点击的信息。</param>
        private void toolStripLabel1_Click(object sender, EventArgs e)
        {
            // 切换发送标志的状态
            gTxShowFlag = !gTxShowFlag;

            if (gTxShowFlag)
            {
                // 创建空对象，模拟鼠标点击事件
                object obj = null;
                MouseEventArgs me = null;

                // 更新时间戳按钮的文本为“OFF”
                toolStripButton_Timestamp.Text = "OFF";

                // 触发时间戳按钮的鼠标按下事件
                toolStripButton_Timestamp_MouseDown(obj, me);

                // 更改标签的字体颜色为红色，表示激活状态
                toolStripLabel1.ForeColor = Color.Red;
            }
            else
            {
                // 更改标签的字体颜色为黑色，表示非激活状态
                toolStripLabel1.ForeColor = Color.Black;
            }
        }


        /// <summary>
        /// 处理工具条的双击事件，切换发送列表按键的可编辑状态。
        /// </summary>
        /// <param name="sender">事件的发送者，通常是工具条控件。</param>
        /// <param name="e">事件参数，包含有关双击事件的信息。</param>
        private void toolStrip_TableTX_DoubleClick(object sender, EventArgs e)
        {
            // 检查当前发送列表按键的状态
            if (toolStrip_TableTX.Text == "发送列表的按键可编辑")
            {
                // 如果可编辑，则设置为不可编辑状态
                gCfg.TextboxTXClickBypassCfg(true);
                toolStrip_TableTX.Text = "发送列表的按键已上锁"; // 更新状态文本
            }
            else
            {
                // 如果不可编辑，则设置为可编辑状态
                gCfg.TextboxTXClickBypassCfg(false);
                toolStrip_TableTX.Text = "发送列表的按键可编辑"; // 更新状态文本
            }

            // 在接收文本框中添加状态变化的提示信息
            richTextBox_Rx.SelectionColor = Color.Gold; // 设置文本颜色为金色
            richTextBox_Rx.AppendText(toolStrip_TableTX.Text + "!!!"); // 添加状态信息
            richTextBox_Rx.SelectionColor = Color.White; // 恢复文本颜色为白色
            richTextBox_Rx.AppendText(System.Environment.NewLine); // 添加换行
        }



        /// <summary>
        /// 处理鼠标悬停事件，当鼠标悬停在发送列表帮助控件上时显示提示信息。
        /// </summary>
        /// <param name="sender">事件的发送者，通常是帮助控件。</param>
        /// <param name="e">事件参数，包含有关鼠标悬停事件的信息。</param>
        private void TxListHelp_MouseHover(object sender, EventArgs e)
        {
            // 创建一个新的点，表示提示框相对于发送列表帮助控件的位置
            Point point = new Point(0, 50);

            // 显示提示信息，持续800毫秒
            toolTip_TxList.Show("[HEX发送]+[延时框]+[报文编辑框]",
                (IWin32Window)sender, // 将发送者作为提示框的窗口
                point,                // 提示框的位置
                800);                 // 提示框显示的持续时间（毫秒）
        }


        /// <summary>
        /// 处理“加载发送列表”菜单项的点击事件。
        /// </summary>
        /// <param name="sender">事件的发送者，通常是菜单项。</param>
        /// <param name="e">事件参数，包含有关点击事件的信息。</param>
        private void 加载发送列表ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // 加载发送列表到表格面板，并检查返回值是否为0
            if (gCfg.TxListLoad(tableLayoutPanel1, tableLayoutPanel2) == 0)
                return; // 如果加载失败，退出方法

            // 注册发送列表的事件
            TxListEventReg();

            // 设置分隔容器的大小，以适应发送列表的数量
            splitContainer1.Size = new Size(246, (20 * gCfg.TxListNum) + 200);

            // 更新工具栏文本框以显示发送列表的数量
            toolStripTextBox_TxListNum.Text = gCfg.TxListNum.ToString();

            // 加载简单的发送列表
            加载简单的发送列表();

            // 加载超级报文
            加载超级报文();

            // 加载自动下发指令的规则
            加载自动下发指令的规则();
        }


        /// <summary>
        /// 处理“另存为”菜单项的点击事件，将当前发送列表另存为指定文件。
        /// </summary>
        /// <param name="sender">事件的发送者，通常是菜单项。</param>
        /// <param name="e">事件参数，包含有关点击事件的信息。</param>
        private void 另存为ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // 调用配置对象的保存方法，将发送列表保存为指定的文件名
            gCfg.TxListOtherSave(toolStripTextBox_TxListNum.Text);
        }


        /// <summary>
        /// 处理“新建”菜单项的点击事件，创建一个新的发送列表。
        /// </summary>
        /// <param name="sender">事件的发送者，通常是菜单项。</param>
        /// <param name="e">事件参数，包含有关点击事件的信息。</param>
        private void 新建ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // 调用配置对象的方法创建一个新的发送列表，并传入当前的表格和文本框内容
            if (gCfg.TxListNewCreate(tableLayoutPanel1, tableLayoutPanel2, toolStripTextBox_TxListNum.Text))
            {
                // 如果创建成功，注册事件并更新分隔容器的大小
                TxListEventReg();
                splitContainer1.Size = new Size(246, (20 * gCfg.TxListNum) + 200);
            }
        }


        /// <summary>
        /// 处理串口选择下拉框的下拉事件，更新可用串口列表。
        /// </summary>
        /// <param name="sender">事件的发送者，通常是串口下拉框。</param>
        /// <param name="e">事件参数，包含有关下拉事件的信息。</param>
        private void toolStripComboBox_SerialPort_DropDown(object sender, EventArgs e)
        {
            // 调用方法更新串口下拉框中的串口列表
            SerialPortNumberUpadta(toolStripComboBox_SerialPort);
        }


        /// <summary>
        /// 处理工具条按钮点击事件，创建频道列表。
        /// </summary>
        /// <param name="sender">事件的发送者，通常是工具条按钮。</param>
        /// <param name="e">事件参数，包含有关点击事件的信息。</param>
        private void toolStripButton4_Click(object sender, EventArgs e)
        {
            // 调用方法创建频道列表
            ChannelListCreate();
        }


        [DllImport("User32")]
        public extern static void SetCursorPos(int x, int y);
        [DllImport("User32")]
        public extern static bool GetCursorPos(ref Point point);


        /// <summary>
        /// 不同数据类型的转换
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void toolStripComboBox_Convert_TextUpdate(object sender, EventArgs e)
        {
            if (toolStripComboBox_Convert.Text == "")
                return;
            int pos = toolStripComboBox_Convert.SelectionStart;
            if (toolStripComboBox_Convert.DroppedDown == false)
            {
                toolStripComboBox_Convert.Items.Clear();
                toolStripComboBox_Convert.SelectionStart = pos;
                string str = "";
                toolStripComboBox_Convert.Items.Add(str);
                toolStripComboBox_Convert.Items.Add(str);
                toolStripComboBox_Convert.Items.Add(str);
                toolStripComboBox_Convert.Items.Add(str);
                toolStripComboBox_Convert.Items.Add(str);
                toolStripComboBox_Convert.DroppedDown = true;
                this.Cursor = Cursor;
                Point point = new Point();
                GetCursorPos(ref point);
                SetCursorPos(point.X, point.Y - 35);
            }
            UInt32 u32 = 0;
            toolStripComboBox_Convert.Text = toolStripComboBox_Convert.Text.ToUpper();
            toolStripComboBox_Convert.SelectionStart = pos;
            try
            {
                if (toolStripComboBox_Convert.Text.Length == 1 &&
                    (toolStripComboBox_Convert.Text[0] == 'X' ||
                    toolStripComboBox_Convert.Text[0] == 'D' ||
                    toolStripComboBox_Convert.Text[0] == 'S'))
                    return;
                switch (toolStripComboBox_Convert.Text[0])
                {
                    case 'X': //16进制输入
                        u32 = Convert.ToUInt32(toolStripComboBox_Convert.Text.Remove(0, 1), 16);
                        break;
                    case 'S': //浮点数输入
                        float f = float.Parse(toolStripComboBox_Convert.Text.Remove(0, 1));
                        byte[] fByte = BitConverter.GetBytes(f);
                        u32 = BitConverter.ToUInt32(fByte, 0);
                        break;
                    case 'D': //整型输入
                        u32 = Convert.ToUInt32(toolStripComboBox_Convert.Text.Remove(0, 1));
                        break;
                    default: //默认整型输入
                        u32 = Convert.ToUInt32(toolStripComboBox_Convert.Text);
                        break;
                }
                string b = Convert.ToString(u32, 2).PadLeft(1);
                for (int cnt = b.Length - 4; cnt > 0; cnt -= 4)
                {
                    b = b.Insert(cnt, ",");
                }
                //显示二进制
                toolStripComboBox_Convert.Items[0] = b;
                //显示u32
                toolStripComboBox_Convert.Items[1] = u32.ToString();
                //显示int32
                toolStripComboBox_Convert.Items[2] = "[int] " + ((int)u32).ToString();
                //16进制显示
                toolStripComboBox_Convert.Items[3] = "0x" + u32.ToString("X");
                //浮点数显示
                toolStripComboBox_Convert.Items[4] = "[float] " + (BitConverter.ToSingle(BitConverter.GetBytes(u32), 0)).ToString();
            }
            catch
            {
                if (pos > 0)
                {
                    toolStripComboBox_Convert.Text = toolStripComboBox_Convert.Text.Remove(pos - 1, 1);
                    toolStripComboBox_Convert.SelectionStart = pos - 1;
                }
            }
        }


        /// <summary>
        /// 快速创建超级报文，按钮触发右键菜单
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void toolStripButton2_Click(object sender, EventArgs e)
        {
            contextMenu.CreateSuperMsgMenu.Show(Control.MousePosition);
        }


        /// <summary>
        /// 创建并初始化一个超级消息实例，并将其添加到指定的控件集合中.
        /// </summary>
        /// <param name="rx_textbox">接收文本框的索引.</param>
        /// <param name="tx_textbox">发送文本框的索引.</param>
        private void CreateSuperMsg_Handler(int rx_textbox, int tx_textbox)
        {
            // 创建一个新的超级消息实例
            SuperMsg superMsg = new SuperMsg();

            // 将当前窗体引用赋值给超级消息实例
            superMsg.formMain = this;

            // 初始化超级消息实例，传入接收和发送文本框的索引
            superMsg.SuperMsgInit(rx_textbox, tx_textbox);

            // 将超级消息的面板添加到流布局面板中
            flowLayoutPanel_SuperMsg.Controls.Add(superMsg.panelMsg);
            // 可以在此处添加更多的操作
        }

        /// <summary>
        /// 注册超级消息对象，将其添加到超级消息列表中.
        /// </summary>
        /// <param name="obj">要添加的超级消息对象.</param>
        public void SuperMsgListReg(object obj)
        {
            // 将提供的超级消息对象添加到列表中
            SuperMsgList.Add(obj);
        }

        /// <summary>
        /// 从超级消息列表中删除指定的超级消息对象.
        /// </summary>
        /// <param name="obj">要删除的超级消息对象.</param>
        public void SuperMsgListDelete(object obj)
        {
            // 从列表中移除提供的超级消息对象
            SuperMsgList.Remove(obj);
        }

        /// <summary>
        /// 处理窗体关闭事件.
        /// 在窗体关闭时关闭实时曲线并退出应用程序.
        /// </summary>
        /// <param name="sender">触发事件的对象.</param>
        /// <param name="e">包含事件数据的参数.</param>
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            // 关闭实时曲线
            realTimeCurve.RT_Curve_Off();

            // 退出应用程序
            System.Environment.Exit(0);
        }


        /// <summary>
        /// 处理自动发送命令更新的点击事件.
        /// 根据文本框的内容更新命令或弹出提示信息.
        /// </summary>
        /// <param name="sender">触发事件的对象.</param>
        /// <param name="e">包含事件数据的参数.</param>
        private void toolStripButton_AutoTx_Update_Click(object sender, EventArgs e)
        {
            // 检查是否是用户点击触发的事件
            if (sender is ToolStripButton)
            {
                // 检查文本框是否为空或者只包含空白字符
                if (!string.IsNullOrWhiteSpace(textBox_AutoTx.Text))
                {
                    // 如果文本框有内容，更新命令
                    AutoTx.CmdUpdate(textBox_AutoTx.Lines, toolStripButton_TxHEX.Text, MasterMsgEnd.Text);
                }
                else
                {
                    // 如果文本框为空，弹出提示信息
                    MessageBox.Show("请在文本框中输入内容后再更新命令。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }
            }
        }

        /// <summary>
        /// 处理波特率下拉框的点击事件.
        /// </summary>
        /// <param name="sender">触发事件的对象.</param>
        /// <param name="e">包含事件数据的参数.</param>
        private void toolStripComboBox_BaudRate_Click(object sender, EventArgs e)
        {
            // 此方法目前为空，您可以在这里添加处理波特率选择的逻辑
        }

        /// <summary>
        /// 处理文本框内容变化的事件.
        /// </summary>
        /// <param name="sender">触发事件的对象.</param>
        /// <param name="e">包含事件数据的参数.</param>
        private void TextBox_Tx_TextChanged(object sender, EventArgs e)
        {
            // 此方法目前为空，您可以在这里添加处理文本框内容变化的逻辑
        }


        /// <summary>
        /// 处理时间戳按钮点击事件.
        /// </summary>
        /// <param name="sender">触发事件的对象.</param>
        /// <param name="e">包含事件数据的参数.</param>
        private void toolStripButton_Timestamp_Click(object sender, EventArgs e)
        {
            // 此方法目前为空，您可以在这里添加处理时间戳按钮点击的逻辑
        }

        /// <summary>
        /// 处理折叠按钮点击事件.
        /// </summary>
        /// <param name="sender">触发事件的对象.</param>
        /// <param name="e">包含事件数据的参数.</param>
        private void toolStripButton_Fold_Click(object sender, EventArgs e)
        {
            // 此方法目前为空，您可以在这里添加处理折叠按钮点击的逻辑
        }


        /// <summary>
        /// 处理标签点击事件.
        /// </summary>
        /// <param name="sender">触发事件的对象.</param>
        /// <param name="e">包含事件数据的参数.</param>
        private void label2_Click(object sender, EventArgs e)
        {
            // 此方法目前为空，您可以在这里添加标签点击的处理逻辑
        }


        /// <summary>
        /// 处理接收频率标签的点击事件.
        /// </summary>
        /// <param name="sender">触发事件的对象.</param>
        /// <param name="e">包含事件数据的参数.</param>
        private void lblReceiveFrequency_Click(object sender, EventArgs e)
        {
            // 此方法目前为空，您可以在这里添加接收频率标签点击的处理逻辑
        }

        /// <summary>
        /// 处理主分割容器第一个面板的绘制事件.
        /// </summary>
        /// <param name="sender">触发事件的对象.</param>
        /// <param name="e">包含绘制事件数据的参数.</param>
        private void splitContainer_Master_Panel1_Paint(object sender, PaintEventArgs e)
        {
            // 此方法目前为空，您可以在这里添加面板绘制的自定义逻辑
        }

        /// <summary>
        /// 处理第一个选项卡的点击事件.
        /// </summary>
        /// <param name="sender">触发事件的对象.</param>
        /// <param name="e">包含事件数据的参数.</param>
        private void tabPage1_Click(object sender, EventArgs e)
        {
            // 此方法目前为空，您可以在这里添加选项卡点击的自定义逻辑
        }

        private void richTextBox_Rx_TextChanged(object sender, EventArgs e)
        {

        }

        private void toolStripComboBox_SerialPort_Click(object sender, EventArgs e)
        {

        }
    }
}
